package systemctl

import (
	"context"
	"errors"
	"fmt"
	"path/filepath"
	"strings"
	"time"

	"github.com/1Panel-dev/1Panel/backend/global"
)

type ServiceConfig struct {
	ServiceName map[string]string
}

type ServiceHandler struct {
	config  *ServiceConfig
	manager ServiceManager
}

func NewServiceHandler(serviceNames map[string]string) *ServiceHandler {
	mgr := GetGlobalManager()
	if mgr == nil {
		global.LOG.Error("failed to get global service manager when creating ServiceHandler")
		return nil
	}
	return &ServiceHandler{
		config: &ServiceConfig{
			ServiceName: serviceNames,
		},
		manager: mgr,
	}
}

type ServiceStatus struct {
	IsActive  bool   `json:"isActive"`
	IsEnabled bool   `json:"isEnabled"`
	IsExists  bool   `json:"isExists"`
	Output    string `json:"output"`
}
type ServiceIsActive struct {
	IsActive bool   `json:"isActive"`
	Output   string `json:"output"`
}

type ServiceIsEnabled struct {
	IsEnabled bool   `json:"isEnabled"`
	Output    string `json:"output"`
}

type ServiceResult struct {
	Success bool   `json:"success"`
	Message string `json:"message"`
	Output  string `json:"output"`
}

var (
	BinaryPath         = "/usr/local/bin" // 1panl service default path
	ErrServiceNotExist = errors.New("service does not exist")
)

func defaultServiceConfig(serviceName string) map[string]string {
	mgr := getManagerName()
	if mgr == "" {
		global.LOG.Error("failed to get manager name for default service config")
		return nil
	}
	return map[string]string{
		mgr: serviceName,
	}
}

func (h *ServiceHandler) ManagerName() string { return h.manager.Name() }
func getManagerName() string {
	if mgr := GetGlobalManager(); mgr != nil {
		return mgr.Name()
	}
	global.LOG.Error("failed to get global service manager")
	return ""
}

func (h *ServiceHandler) GetServiceName() string {
	manager := h.ManagerName()
	if manager == "" {
		global.LOG.Error("manager name is empty when getting service name")
		return ""
	}
	return h.config.ServiceName[manager]
}

func (h *ServiceHandler) GetServicePath() (string, error) {
	manager := h.ManagerName()
	serviceName := h.config.ServiceName[manager]

	if serviceName == "" {
		err := fmt.Errorf("service name not found for %s", manager)
		global.LOG.Errorf("GetServicePath error: %v", err)
		return "", err
	}

	cleanPath := filepath.Clean(serviceName)
	if strings.Contains(cleanPath, "..") {
		err := fmt.Errorf("invalid path: %q", cleanPath)
		global.LOG.Errorf("GetServicePath security check failed: %v", err)
		return "", err
	}
	switch manager {
	case "systemd":
		return findSystemdPath(cleanPath)
	case "openrc", "sysvinit":
		return checkInitDPath(cleanPath)
	default:
		err := fmt.Errorf("unsupported init system: %s", manager)
		global.LOG.Errorf("GetServicePath error: %v", err)
		return "", err
	}
}

func findSystemdPath(name string) (string, error) {
	paths := []string{"/etc/systemd/system", "/usr/lib/systemd/system",
		"/usr/share/systemd/system", "/usr/local/lib/systemd/system"}

	for _, p := range paths {
		if path := filepath.Join(p, name); FileExist(path) {
			return path, nil
		}
	}
	err := fmt.Errorf("service path not found for %s", name)
	global.LOG.Errorf("findSystemdPath error: %v", err)
	return "", err
}

func checkInitDPath(name string) (string, error) {
	path := filepath.Join("/etc/init.d", name)
	if !FileExist(path) {
		err := fmt.Errorf("service path not found for %s", name)
		global.LOG.Errorf("checkInitDPath error: %v", err)
		return "", err
	}
	return path, nil
}

func (h *ServiceHandler) ExecuteAction(action string) (ServiceResult, error) {
	successMsg := fmt.Sprintf("%s : %s completed", action, h.GetServiceName())
	return h.executeAction(action, successMsg)
}

func (h *ServiceHandler) IsExists() (ServiceStatus, error) {
	manager := GetGlobalManager()
	isExist, _ := manager.ServiceExists(h.config)
	return ServiceStatus{
		IsExists: isExist,
	}, nil
}
func (h *ServiceHandler) IsActive() (ServiceStatus, error) {
	manager := GetGlobalManager()
	if manager == nil {
		global.LOG.Error("service manager not initialized during active check")
		return ServiceStatus{}, fmt.Errorf("service manager not initialized")
	}
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	activeCmd, err := manager.BuildCommand("status", h.config)
	if err != nil {
		global.LOG.Errorf("Build status command failed: %v", err)
		return ServiceStatus{}, fmt.Errorf("build status command failed: %w", err)
	}

	output, err := executeCommand(ctx, activeCmd[0], activeCmd[1:]...)
	if err != nil {
		if strings.Contains(err.Error(), "inactive") {
			return ServiceStatus{
				IsExists: false,
			}, nil
		}
		global.LOG.Errorf("Active check execution failed: %v", err)
		return ServiceStatus{}, fmt.Errorf("status check failed: %w", err)
	}

	isActive, err := manager.ParseStatus(string(output), h.config, "active")
	if err != nil {
		global.LOG.Warnf("Status parse error: %v", err)
	}
	return ServiceStatus{
		IsActive: isActive,
		Output:   string(output),
	}, nil
}

func (h *ServiceHandler) IsEnabled() (ServiceStatus, error) {
	manager := GetGlobalManager()
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	enabledCmd, err := manager.BuildCommand("is-enabled", h.config)
	if err != nil {
		if strings.Contains(err.Error(), "not found") {
			return ServiceStatus{
				IsEnabled: false,
			}, nil
		}
		global.LOG.Errorf("Build is-enabled command failed: %v", err)
		return ServiceStatus{}, fmt.Errorf("build enabled check command failed: %w", err)
	}

	output, err := executeCommand(ctx, enabledCmd[0], enabledCmd[1:]...)
	if err != nil {
		if strings.Contains(err.Error(), "disabled") {
			return ServiceStatus{
				IsEnabled: false,
			}, nil
		}
		// 	// isEnabled, err := h.ParseStatus(string(output), h.config, "enabled")
		// global.LOG.Errorf("Enabled check execution failed: %v", err)
		// return ServiceStatus{}, fmt.Errorf("enabled check failed: %w", err)
	}

	isEnabled, err := manager.ParseStatus(string(output), h.config, "enabled")
	if err != nil {
		global.LOG.Warnf("Enabled status parse error: %v", err)
	}
	return ServiceStatus{
		IsEnabled: isEnabled,
		Output:    string(output),
	}, nil
}

func (h *ServiceHandler) StartService() (ServiceResult, error) {
	return h.ExecuteAction("start")
}

func (h *ServiceHandler) StopService() (ServiceResult, error) {
	return h.ExecuteAction("stop")
}

func (h *ServiceHandler) RestartService() (ServiceResult, error) {
	return h.ExecuteAction("restart")
}

func (h *ServiceHandler) EnableService() (ServiceResult, error) {
	return h.ExecuteAction("enable")
}

func (h *ServiceHandler) DisableService() (ServiceResult, error) {
	return h.ExecuteAction("disable")
}

func (h *ServiceHandler) executeAction(action, successMsg string) (ServiceResult, error) {
	manager := GetGlobalManager()
	if manager == nil {
		global.LOG.Error("service manager not initialized during action execution")
		return ServiceResult{}, fmt.Errorf("service manager not initialized")
	}

	ctx, cancel := context.WithTimeout(context.Background(), defaultCommandTimeout)
	defer cancel()

	cmdArgs, err := manager.BuildCommand(action, h.config)
	if err != nil {
		global.LOG.Errorf("Build command failed for action %s: %v", action, err)
		return ServiceResult{}, fmt.Errorf("build command failed: %w", err)
	}

	output, err := executeCommand(ctx, cmdArgs[0], cmdArgs[1:]...)
	if err != nil {
		global.LOG.Errorf("%s operation failed: %v", action, err)
		return ServiceResult{
			Success: false,
			Message: fmt.Sprintf("%s failed", action),
			Output:  string(output),
		}, fmt.Errorf("%s operation failed: %w", action, err)
	}

	global.LOG.Infof("[%s]: %s", manager.Name(), successMsg)
	return ServiceResult{
		Success: true,
		Message: successMsg,
		Output:  string(output),
	}, nil
}
